#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>

#define MPU9250_WHO_AM_I 0x75
#define MPU9250_ID 0x71

struct mpu9250_data {
    struct i2c_client *client;
    struct iio_trigger *trig;
};

static const struct iio_chan_spec mpu9250_channels[] = {
    {
        .type = IIO_ACCEL,
        .channel2 = IIO_MOD_X,
        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
    },
};

static int mpu9250_read_reg(struct i2c_client *client, u8 reg, u8 *val)
{
    int ret = i2c_smbus_read_byte_data(client, reg);
    if (ret < 0)
        return ret;
    *val = ret;
    return 0;
}

static int mpu9250_read_raw(struct iio_dev *indio_dev,struct iio_chan_spec const *chan,int *val, int *val2, long mask)
{
    struct mpu9250_data *data = iio_priv(indio_dev);
    u8 buffer[6];
    int ret;

    switch (mask) {
    case IIO_CHAN_INFO_RAW:
        ret = i2c_smbus_read_i2c_block_data(data->client, 0x3B, 6, buffer);
        *val = (buffer[0] << 8) | buffer[1];
        return IIO_VAL_INT;
    }
    return -EINVAL;
}

static const struct iio_info mpu9250_info = {
    .read_raw = mpu9250_read_raw,
};

static int mpu9250_probe(struct i2c_client *client,
                        const struct i2c_device_id *id)
{
    struct iio_dev *indio_dev;
    struct mpu9250_data *data;
    u8 whoami;
    int ret;

    ret = mpu9250_read_reg(client, MPU9250_WHO_AM_I, &whoami);
    if (ret || whoami != MPU9250_ID)
        return -ENODEV;

    indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
    if (!indio_dev)
        return -ENOMEM;

    data = iio_priv(indio_dev);
    data->client = client;

    indio_dev->name = "mpu9250";
    indio_dev->channels = mpu9250_channels;
    indio_dev->num_channels = ARRAY_SIZE(mpu9250_channels);
    indio_dev->info = &mpu9250_info;
    indio_dev->modes = INDIO_DIRECT_MODE;

    ret = devm_iio_device_register(&client->dev, indio_dev);
    if (ret)
        return ret;

    return 0;
}

static const struct of_device_id mpu9250_of_match[] = {
    { .compatible = "invensense,mpu9250" },
    {}
};
MODULE_DEVICE_TABLE(of, mpu9250_of_match);

static struct i2c_driver mpu9250_driver = {
    .driver = {
        .name = "mpu9250",
        .of_match_table = mpu9250_of_match,
    },
    .probe = mpu9250_probe,
};
module_i2c_driver(mpu9250_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MPU9250 IMU Driver");